package logs

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"text/template"
	"time"

	"ckeadm/constants"
	"ckeadm/util"
	"ckeadm/util/service"
	"github.com/sirupsen/logrus"
)

var CkeadmWaitServiceTemplate = template.Must(template.New("join").Parse(`
[Unit]
Description=ckeadm-wait
Documentation=http://git.inspur.com
After=network.target

[Service]
ExecStart={{.CkeadmPath}} wait \
          --port={{.ListenPort}} \
          --address={{.ListenAddress}} \
          --following={{.Following}} \
          --cachedir={{.CkeadmCacheDir}} \
          --private-key={{.PrivateKey}} \
          --address-list={{.AddressList}}
LimitNOFILE=5000
LimitNPROC=5000
TimeoutStartSec=1min
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s

[Install]
WantedBy=multi-user.target
`[1:]))

type CkeadmWaitService struct {
	ServiceName    string
	CkeadmPath     string
	ListenAddress  string
	ListenPort     int
	CkeadmCacheDir string
	Following      bool
	PrivateKey     string
	AddressList    string
}

func NewCkeadmWaitService(ServiceName, CkeadmPath, ListenAddress string, ListenPort int, CkeadmCacheDir string, Following bool, PrivateKey, AddressList string) svc.Service {
	return &CkeadmWaitService{
		ListenAddress:  ListenAddress,
		ServiceName:    ServiceName,
		ListenPort:     ListenPort,
		CkeadmCacheDir: CkeadmCacheDir,
		CkeadmPath:     CkeadmPath,
		Following:      Following,
		PrivateKey:     PrivateKey,
		AddressList:    AddressList,
	}
}

// TODO: completed ckeck service config for ckeadm wait
func (wait *CkeadmWaitService) CheckServiceConfig() bool {
	return false
}

func (wait *CkeadmWaitService) CreateAndStartService(templates *template.Template, execPath string) error {
	logrus.Infof("trying to create service %s, listening and service in: %s:%d\n", wait.ServiceName, wait.ListenAddress, wait.ListenPort)

	var buff = bytes.NewBuffer([]byte{})
	_ = templates.Execute(buff, wait)

	service, err := ioutil.ReadAll(buff)
	if err != nil {
		return fmt.Errorf("error resolve template: %s", err.Error())
	}
	if err = util.WriteFile(fmt.Sprintf("/etc/systemd/system/%s.service", wait.ServiceName), string(service)); err != nil {
		return fmt.Errorf("error write service: %s", err.Error())
	}

	if _, err = os.Stat(wait.CkeadmPath); err != nil || constants.CkeadmOverwrite {
		// #nosec
		exec.Command("cp", "-f", execPath, wait.CkeadmPath).Run()
	}

	logrus.Infof("trying to enabled and start service %s ...\n", wait.ServiceName)
	if err = wait.RestartService(); err != nil {
		logrus.Warningf("service %s restart failed, reasion: %s\n", wait.ServiceName, err.Error())
	}

	logrus.Infof("wait for service become healthy: %s\n", wait.ServiceName)
	return wait.WaitForServiceRunning()
}

func (wait *CkeadmWaitService) RestartService() error {
	// #nosec
	exec.Command("systemctl", "daemon-reload").Run()
	// #nosec
	exec.Command("systemctl", "enable", wait.ServiceName).Run()
	// #nosec
	return exec.Command("systemctl", "restart", wait.ServiceName).Run()
}

func (wait *CkeadmWaitService) IsRunning() bool {
	reps, err := (&http.Client{
		Transport: &http.Transport{
			DisableKeepAlives:     true,
			ResponseHeaderTimeout: time.Second,
		},
	}).Get(fmt.Sprintf("http://%s:%d%s", wait.ListenAddress, wait.ListenPort, constants.CkeadmWaitHealthzPattern))

	if err != nil {
		return false
	}
	body, err := ioutil.ReadAll(reps.Body)
	defer reps.Body.Close()

	return string(body) == "true"
}

func (wait *CkeadmWaitService) WaitForServiceRunning() error {
	// wait for service start util restart timeout
	return util.PollImmediate(constants.WaitCallRetryInterval, constants.WaitRestartTimeout, func() (bool, error) {
		return wait.IsRunning(), nil
	})
}

func (wait *CkeadmWaitService) RemoveService() error {
	// #nosec
	exec.Command("systemctl", "stop", wait.ServiceName).Run()
	// #nosec
	exec.Command("systemctl", "disable", wait.ServiceName).Run()
	return os.Remove(fmt.Sprintf("/etc/systemd/system/%s.service", wait.ServiceName))
}
